package com.redstars.generate.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.redstars.generate.entity.SysBaseClass;
import com.redstars.generate.entity.SysTable;
import com.redstars.generate.exception.RedstarsException;
import com.redstars.generate.query.SysGenerateTemplateQuery;
import com.redstars.generate.service.*;
import com.redstars.generate.utils.DateUtils;
import com.redstars.generate.utils.TemplateUtils;
import com.redstars.generate.vo.SysGenerateTemplateVo;
import com.redstars.generate.vo.SysPreviewFileVo;
import com.redstars.generate.vo.SysTableFieldVo;
import com.redstars.generate.vo.SysTableVo;
import freemarker.template.TemplateException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author : zhouhx
 * @date : 2022/10/18 15:20
 */
@Service
@Slf4j
@AllArgsConstructor
public class SysGeneratorServiceImpl implements SysGeneratorService {
    private final SysTableService tableService;
    private final SysTableFieldService tableFieldService;
    private final SysDatasourceService datasourceService;
    private final SysBaseClassService baseClassService;
    private final SysFieldTypeService fieldTypeService;
    private final SysGenerateTemplateService templateService;

    private static Map<String,String> mybatisPlusContants = new HashMap<String,String>();
    {
        mybatisPlusContants.put("=","eq");
        mybatisPlusContants.put("!=","ne");
        mybatisPlusContants.put(">","gt");
        mybatisPlusContants.put(">=","ge");
        mybatisPlusContants.put("<","lt");
        mybatisPlusContants.put("<=","le");
        mybatisPlusContants.put("like","like");
        mybatisPlusContants.put("left like","likeLeft");
        mybatisPlusContants.put("right like","likeRight");
    }

    @Override
    public void download(String tableIds, HttpServletResponse response) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        // 生成代码
        for (String tableId : tableIds.split(",")) {
            this.downloadCode(Long.parseLong(tableId), zip);
        }

        IoUtil.close(zip);

        // zip压缩包数据
        byte[] data = outputStream.toByteArray();

        response.reset();
        response.setHeader("Content-Disposition", "attachment; filename=\"generate.zip\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");

        IoUtil.write(response.getOutputStream(), false, data);
    }

    @Override
    public void downloadCode(Long tableId, ZipOutputStream zip) {
        //获取内置环境变量
        Map<String, Object> dataModel = getInEnvironment(tableId);
        //获取模板
        List<SysGenerateTemplateVo> selectTemplates = templateService.getSelectTemplate(tableId);
        if(CollUtil.isEmpty(selectTemplates)){
            throw new RedstarsException("没有配置可用的模板");
        }

        // 渲染模板并输出
        for (SysGenerateTemplateVo template : selectTemplates) {
            dataModel.put("templateName", template.getTemplateName());
            String content = null;
            String path = null;
            try {
                content = TemplateUtils.getContent(template.getTemplateContent(), dataModel);
                path = TemplateUtils.getContent(template.getTemplateRoute(), dataModel);
            } catch (Exception e) {
               throw new RedstarsException("渲染模板失败，请检查模板语法");
            }

            try {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(path));
                IoUtil.writeUtf8(zip, false, content);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                throw new RedstarsException("模板写入失败：" + path, e);
            }
        }
    }

    @NotNull
    //获取内置环境变量
    private Map<String, Object> getInEnvironment(Long tableId) {
        // 数据模型
        Map<String, Object> dataModel = getDataModel(tableId);

        List<SysTableFieldVo> gridList = (List<SysTableFieldVo>) dataModel.get("gridList");
        //设置生成的查询字段
        if(ObjectUtil.isNotEmpty(gridList)){
            gridList.forEach(grid ->{
                String queryType = grid.getQueryType();
                String attrName = grid.getAttrName();
                grid.setPlusQueryType(mybatisPlusContants.get(queryType));
                grid.setGetter(String.format("get%s%s",attrName.substring(0,1).toUpperCase(),attrName.substring(1)));
            });
        }
        return dataModel;
    }

    @Override
    public Map<String, Object> getDataModel(Long tableId) {
        // 表信息
        SysTableVo table = tableService.detail(tableId);

        // 数据模型
        Map<String, Object> dataModel = new HashMap<>();

        // 获取数据库类型
        String dbType = datasourceService.getDatabaseProductName(table.getDatasourceId());
        dataModel.put("dbType", dbType);

        // 项目信息
        dataModel.put("package", table.getPackageName());
        dataModel.put("packagePath", table.getPackageName().replace(".", File.separator));
        dataModel.put("moduleName", table.getModuleName());
        dataModel.put("ModuleName", StrUtil.upperFirst(table.getModuleName()));
        dataModel.put("functionName", table.getFunctionName());
        dataModel.put("FunctionName", StrUtil.upperFirst(table.getFunctionName()));
        //后端路径
        dataModel.put("backendPath",StrUtil.isEmpty(table.getBackendPath())?"":table.getBackendPath());
        //前端路径
        dataModel.put("frontendPath",StrUtil.isEmpty(table.getFrontendPath())?"":table.getFrontendPath());


        // 开发者信息
        dataModel.put("author", table.getAuthor());
        dataModel.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
        dataModel.put("date", DateUtils.format(new Date(), DateUtils.DATE_PATTERN));

        // 设置字段分类
        setFieldTypeList(dataModel, table);
        // 设置基类信息
        setBaseClass(dataModel, table);
        // 导入的包列表
        Set<String> importList = fieldTypeService.getPackageByTableId(table.getId());
        dataModel.put("importList", importList);

        // 表信息
        dataModel.put("tableName", table.getTableName());
        dataModel.put("tableComment", table.getTableComment());
        dataModel.put("ClassName", StrUtil.upperFirst(table.getClassName()));
        dataModel.put("className", StrUtil.lowerFirst(table.getClassName()));
        dataModel.put("fieldList", table.getFieldList());
        //子表信息
        dataModel.put("subTableList", table.getSubTableList());
        // 生成路径
        dataModel.put("backendPath", table.getBackendPath());
        dataModel.put("frontendPath", table.getFrontendPath());

        return dataModel;
    }

    @Override
    public String test(SysGenerateTemplateVo vo) {
        String templateContent = vo.getTemplateContent();
        String templateName = vo.getTemplateName();
        String templateRoute = vo.getTemplateRoute();
        if(StrUtil.isEmpty(templateContent)){
            return "模板内容为空";
        }
        if(StrUtil.isEmpty(templateName)){
            return "模板名称为空";
        }
        if(StrUtil.isEmpty(templateRoute)){
            return "模板路径为空";
        }
        List<SysTable> tablesList = tableService.list();
        if(CollUtil.isEmpty(tablesList)){
            return "请在代码生成菜单里导入一个测试表";
        }
        Long tableId = tablesList.get(0).getId();
        //获取内置环境变量
        Map<String, Object> dataModel = getInEnvironment(tableId);
        // 渲染模板并输出
        String content = null;
        try {
            dataModel.put("templateName", templateName);
            content = TemplateUtils.getContent(templateContent, dataModel);
            String path = TemplateUtils.getContent(templateRoute, dataModel);
        } catch (Exception e) {
            log.error(""+e);
            return e.getMessage();
        }

        return "成功\n\r"+content;
    }

    @Override
    public void generatorCode(Long[] tableIds) {
        Arrays.stream(tableIds).forEach(tableId ->{
            //获取内置环境变量
            Map<String, Object> dataModel = getInEnvironment(tableId);
            //获取模板
            List<SysGenerateTemplateVo> selectTemplates = templateService.getSelectTemplate(tableId);
            if(CollUtil.isEmpty(selectTemplates)){
                throw new RedstarsException("没有配置可用的模板");
            }

            // 渲染模板并输出
            for (SysGenerateTemplateVo template : selectTemplates) {
                dataModel.put("templateName", template.getTemplateName());
                String content = null;
                String path = null;
                try {
                    content = TemplateUtils.getContent(template.getTemplateContent(), dataModel);
                    path = TemplateUtils.getContent(template.getTemplateRoute(), dataModel);
                } catch (Exception e) {
                    throw new RedstarsException("渲染模板失败，请检查模板语法");
                }

                try {
                    FileUtil.writeUtf8String(content, path);
                } catch (Exception e) {
                    throw new RedstarsException("模板写入失败：" + path, e);
                }
            }
        });
    }

    @Override
    public List<SysPreviewFileVo> preview(Long tableId) {
        //获取内置环境变量
        Map<String, Object> dataModel = getInEnvironment(tableId);
        //获取模板
        List<SysGenerateTemplateVo> selectTemplates = templateService.getSelectTemplate(tableId);
        if(CollUtil.isEmpty(selectTemplates)){
            throw new RedstarsException("没有配置可用的模板");
        }

        // 渲染模板并输出
        List<SysPreviewFileVo> list = new ArrayList<>();
        for (SysGenerateTemplateVo template : selectTemplates) {
            dataModel.put("templateName", template.getTemplateName());
            String content = null;
            String path = null;
            try {
                content = TemplateUtils.getContent(template.getTemplateContent(), dataModel);
                path = TemplateUtils.getContent(template.getTemplateRoute(), dataModel);
            } catch (Exception e) {
                throw new RedstarsException("渲染模板失败，请检查模板语法");
            }

            SysPreviewFileVo fileVo = new SysPreviewFileVo();
            fileVo.setFileName(path.substring(path.lastIndexOf("/")).replaceAll("/",""));
            fileVo.setContent(content);

            list.add(fileVo);
        }
        return list;
    }

    /**
     * 设置字段分类信息
     *
     * @param dataModel 数据模型
     * @param table     表
     */
    private void setFieldTypeList(Map<String, Object> dataModel, SysTableVo table) {
        // 主键列表 (支持多主键)
        List<SysTableFieldVo> primaryList = new ArrayList<>();
        // 表单列表
        List<SysTableFieldVo> formList = new ArrayList<>();
        // 网格列表
        List<SysTableFieldVo> gridList = new ArrayList<>();
        // 查询列表
        List<SysTableFieldVo> queryList = new ArrayList<>();

        for (SysTableFieldVo field : table.getFieldList()) {
            if (Boolean.parseBoolean(field.getPrimaryPk().toString())) {
                primaryList.add(field);
            }
            if (ObjectUtil.isNotEmpty(field.getFormItem())&& field.getFormItem()) {
                formList.add(field);
            }
            if (ObjectUtil.isNotEmpty(field.getGridItem())&&field.getGridItem()) {
                gridList.add(field);
            }
            if (ObjectUtil.isNotEmpty(field.getQueryItem())&&field.getQueryItem()) {
                queryList.add(field);
            }
        }
        dataModel.put("primaryList", primaryList);
        dataModel.put("formList", formList);
        dataModel.put("gridList", gridList);
        dataModel.put("queryList", queryList);
    }


    /**
     * 设置基类信息
     *
     * @param dataModel 数据模型
     * @param table     表
     */
    private void setBaseClass(Map<String, Object> dataModel, SysTableVo table) {
        if (table.getBaseclassId() == null) {
            return;
        }

        // 基类
        SysBaseClass baseClass = baseClassService.getById(table.getBaseclassId());
        baseClass.setPackageName(baseClass.getPackageName());
        dataModel.put("baseClass", baseClass);

        // 基类字段
        String[] fields = baseClass.getFields().split(",");

        // 标注为基类字段
        for (SysTableFieldVo field : table.getFieldList()) {
            field.setBaseField(false);
            if (ArrayUtil.contains(fields, field.getFieldName())) {
                field.setBaseField(true);
            }
        }
    }
}
